﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Test
{
	public static class LinqExtensions
	{
		
		/// <summary>
		/// Casts a collection, at runtime, to a generic (or strongly-typed) collection.
		/// </summary>
		public static System.Collections.IEnumerable Cast(this System.Collections.IEnumerable sourceobjects, Type elementType) {
			IQueryable queryable = sourceobjects.AsQueryable();
			MethodCallExpression castExpression =
				Expression.Call(typeof(Queryable), "Cast", new Type[] { elementType }, Expression.Constant(queryable));
			var lambdaCast = Expression.Lambda(castExpression, Expression.Parameter(typeof(System.Collections.IEnumerable), "z"));
			System.Collections.IEnumerable castresults = (System.Collections.IEnumerable)lambdaCast.Compile().DynamicInvoke(new object[] { queryable });
			return castresults;
		}


		/// <summary>		
		/// </summary>
		/// <typeparam name="TSource">element Type of the first and second collections</typeparam>
		/// <param name="second">second collection to union with</param>
		/// <param name="comparison">IComparer</param>
		/// <returns></returns>
		public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second,
			Comparison<TSource> comparison) {
			IEqualityComparer<TSource> eqcomparer = new EqualityComparer<TSource>(comparison);
			HashSet<TSource> third = new HashSet<TSource>(first);	//can also use SortedList<Tkey,Tvalue>			
			foreach (TSource element in second) {				
				if (!third.Contains(element)) {
					third.Add(element);
				}
			}
			return third;
		}

		/// <summary>
		/// .NET default impl. doesn't automatically use your impl. of IComparable or IEquatable
		/// </summary>
		/// <typeparam name="TSource"></typeparam>
		/// <param name="first"></param>
		/// <param name="comparison"></param>
		/// <returns></returns>		
		public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> first, Comparison<TSource> comparison) {
			IEqualityComparer<TSource> eqcomparer = new EqualityComparer<TSource>(comparison);
			HashSet<TSource> second = new HashSet<TSource>(eqcomparer);	//can also use SortedList<Tkey,Tvalue>			
			foreach (TSource element in first) {
				if (!second.Contains(element)) {
					second.Add(element);
				}
			}	
			return second;
		}

	}

	public class EqualityComparer<T> : IEqualityComparer<T>
	{
		Comparison<T> comparison;
		public EqualityComparer(Comparison<T> comparison) {
			this.comparison = comparison;
		}
		#region IEqualityComparer<T> Members
		public bool Equals(T x, T y) {
			return this.comparison(x, y) == 0;
		}

		public int GetHashCode(T obj) {
			return base.GetHashCode();
		}

		#endregion
	}

}


